package com.shanzmoo.core.service.impl;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.shanzmoo.core.service.IProductBaseRedisService;
import com.shanzmoo.db.product.entity.dto.SpuSearchDto;
import com.shanzmoo.base.constant.Constant;
import com.shanzmoo.core.service.IRedisSearchService;
import com.shanzmoo.core.util.HanlpUtils;
import com.shanzmoo.core.util.RedisCoreKeyUtils;
import com.shanzmoo.base.bean.SResponse;
import com.shanzmoo.base.constant.RedisConstant;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * redis 搜索服务接口
 *
 * @author: Gzhao 2020/12/24
 */
@Slf4j
@Service
public class RedisSearchServiceImpl implements IRedisSearchService {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private IProductBaseRedisService productBaseRedisService;

    @Override
    public SResponse<List<SpuSearchDto>> query(String text) {
        // 1. 对搜索词汇分词
        List<String> wordList = HanlpUtils.segment(text);

        // 2. 根据搜索词汇获取缓存数据
        List<SpuSearchDto> supIdResultMap = searchByList(wordList);

        return SResponse.ok(supIdResultMap);
    }

    @Override
    public SResponse<Boolean> addOne(String spuId, String spuName, boolean updateAll) {

        // 1. 对商品名spuName分词
        List<String> wordList = HanlpUtils.segment(spuName);

        // 2. 存储一份 hash <spuid, spuName> (用于后续的索引更新)
        boolean saveSuccess = saveSpuSearchAssist(spuId, spuName, wordList, updateAll);
        if (!saveSuccess) {
            log.info("spuId: {}, 已在搜索缓存中存在，添加失败", spuId);
            return SResponse.fail(Constant.ERRCODE_LOGIC, "索引添加失败");
        }

        // 3. 更新索引值
        updateSkuSearch(wordList, spuId, updateAll);

        return SResponse.ok(Boolean.TRUE);
    }

    @Override
    public SResponse<Boolean> updateOne(String spuId, String spuName) {
        // 先删
        deleteOne(spuId);
        // 再加
        addOne(spuId, spuName, false);

        return SResponse.ok(Boolean.TRUE);
    }

    @Override
    public SResponse<Boolean> deleteOne(String spuId) {
        // 1. 获取辅助缓存
        String spuSearchAssistKey = RedisCoreKeyUtils.installSpuSearchAssistKey(obtainActiveSpace(false));

        Boolean exist = stringRedisTemplate.opsForHash().hasKey(spuSearchAssistKey, spuId + RedisConstant.SPU_SEARCH_ASSIST_SUFFIX);
        if (!exist) {
            log.error("删除spu：{} 缓存失败，spuId 不存在", spuId);
            return SResponse.fail(Constant.ERRCODE_LOGIC, "缓存中未找到spu");
        }
        String wordJsonStr = (String) stringRedisTemplate.opsForHash().get(spuSearchAssistKey, spuId + RedisConstant.SPU_SEARCH_ASSIST_SUFFIX);

        // 2. 从辅助缓存中获取分词信息
        List<String> wordList = JSON.parseArray(wordJsonStr, String.class);

        // 3. 根据分词信息 和 spuId 值，修改搜索缓存
        String redisKey = RedisCoreKeyUtils.installSpuSearchInfoKey(obtainActiveSpace(false));

        for (String field : wordList) {
            // 获取缓存
            String oldValue = (String) stringRedisTemplate.opsForHash().get(redisKey, field);
            List<String> spuIdList = JSON.parseArray(oldValue, String.class);

            if (spuIdList.size() == 1 && spuIdList.get(0).equals(spuId)) {
                // 缓存中只有当前spuId，直接删除
                stringRedisTemplate.opsForHash().delete(redisKey, field);

            } else {
                // 缓存还存在其他spuId，则移除spuId更新缓存
                List<String> list = JSON.parseArray(oldValue, String.class);
                list.remove(spuId);
                System.out.println(list);
                stringRedisTemplate.opsForHash().put(redisKey, field, JSON.toJSONString(list));
            }
        }

        // 4. 删除辅助缓存
        stringRedisTemplate.opsForHash().delete(spuSearchAssistKey, spuId, spuId + RedisConstant.SPU_SEARCH_ASSIST_SUFFIX);

        return SResponse.ok(Boolean.TRUE);
    }

    @Override
    public SResponse<Boolean> updateAllSpu(Map<String, String> spuMap) {
        log.info("======= 开始全量更新缓存 =======");
        // 1. 添加缓存数据
        for (String spuId : spuMap.keySet()) {
            addOne(spuId, spuMap.get(spuId), true);
        }

        // 2. 更新启用空间
        String space = updateActiveSpace();

        // 3. 删除 更新空间
        removeUpdateSpace(space);

        log.info("======= 全量更新缓存结束 =======");
        return SResponse.ok(Boolean.TRUE);
    }


    private List<SpuSearchDto> searchByList(List wordList) {
        String redisKey = RedisCoreKeyUtils.installSpuSearchInfoKey(obtainActiveSpace(false));

        if (!stringRedisTemplate.hasKey(redisKey)){
            log.warn("未查询到spu缓存数据");
            Map<String, String> spuMap = productBaseRedisService.obtainSearchSpuList();

            updateAllSpu(spuMap);
        }

        List valueList = stringRedisTemplate.opsForHash().multiGet(redisKey, wordList);

        Map<String, Integer> result = new HashMap<>();

        for (Object value : valueList) {
            if (value != null) {
                List<String> spuIdList = JSON.parseArray(value.toString(), String.class);

                for (String spuId : spuIdList) {
                    Integer count = result.get(spuId);
                    result.put(spuId, count == null ? 1 : ++count);
                }
            }
        }
        log.info("===== 搜索spu ===== 搜索分词：{}, 结果：{}", wordList, result);
        return SpuSearchDto.creatList(result);
    }

    /**
     * 更新索引值
     *
     * @param wordList
     */
    private void updateSkuSearch(List<String> wordList, String spuId, boolean updateAll) {
        String redisKey = RedisCoreKeyUtils.installSpuSearchInfoKey(obtainActiveSpace(updateAll));

        for (String field : wordList) {
            String value = (String) stringRedisTemplate.opsForHash().get(redisKey, field);

            List<String> spuIdList = (value == null ? new ArrayList<>() : JSON.parseArray(value, String.class));
            spuIdList.add(spuId);

            stringRedisTemplate.opsForHash().put(redisKey, field, JSON.toJSONString(spuIdList));
        }
    }

    /**
     * 存储一份 hash <spuid, spuName> (用于后续的索引更新)
     *
     * @param spuId
     * @param spuName
     */
    private boolean saveSpuSearchAssist(String spuId, String spuName, List<String> wordList, boolean updateAll) {
        String key = RedisCoreKeyUtils.installSpuSearchAssistKey(obtainActiveSpace(updateAll));

        Boolean exist = stringRedisTemplate.opsForHash().hasKey(key, spuId);
        if (exist) {
            return false;
        }
        stringRedisTemplate.opsForHash().put(key, spuId, spuName);

        // 分词结果缓存
        stringRedisTemplate.opsForHash().put(key, spuId + "_word", JSON.toJSONString(wordList));

        return true;
    }

    /**
     * 获取启用的 商品缓存空间
     *
     * @return
     */
    @Override
    public String obtainActiveSpace(boolean updateAll) {
        String key = RedisCoreKeyUtils.installSpuSearchActiveSpaceKey();
        String value = stringRedisTemplate.opsForValue().get(key);

        if (updateAll) {
            // 全量更新 走更新缓存空间
            if (StrUtil.isBlank(value)) {
                stringRedisTemplate.opsForValue().set(key, RedisConstant.BASE_SPU_SEARCH_SPACE_A);
                return RedisConstant.BASE_SPU_SEARCH_SPACE_B;
            }

            return value.equals(RedisConstant.BASE_SPU_SEARCH_SPACE_A) ? RedisConstant.BASE_SPU_SEARCH_SPACE_B
                    : RedisConstant.BASE_SPU_SEARCH_SPACE_A;
        } else {
            // 非 全量更新操作，走启用的缓存空间
            if (StrUtil.isBlank(value)) {
                stringRedisTemplate.opsForValue().set(key, RedisConstant.BASE_SPU_SEARCH_SPACE_A);
                return RedisConstant.BASE_SPU_SEARCH_SPACE_A;
            }

            return value;
        }
    }

    /**
     * 跟新启用空间
     */
    private String updateActiveSpace() {
        String key = RedisCoreKeyUtils.installSpuSearchActiveSpaceKey();
        String value = stringRedisTemplate.opsForValue().get(key);

        value = value.equals(RedisConstant.BASE_SPU_SEARCH_SPACE_A) ? RedisConstant.BASE_SPU_SEARCH_SPACE_B
                : RedisConstant.BASE_SPU_SEARCH_SPACE_A;

        stringRedisTemplate.opsForValue().set(key, value);

        return value;
    }

    /**
     * 删除 更新空间
     * @param activeSpace
     */
    private void removeUpdateSpace(String activeSpace) {
        String updateSpace = activeSpace.equals(RedisConstant.BASE_SPU_SEARCH_SPACE_A) ? RedisConstant.BASE_SPU_SEARCH_SPACE_B
                : RedisConstant.BASE_SPU_SEARCH_SPACE_A;

        String assitKey = RedisCoreKeyUtils.installSpuSearchAssistKey(updateSpace);
        stringRedisTemplate.delete(assitKey);

        String infoKey = RedisCoreKeyUtils.installSpuSearchInfoKey(updateSpace);
        stringRedisTemplate.delete(infoKey);
    }
}
